﻿using System.Collections.Generic;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using TrenchcoatRobots.Datatypes;

namespace TrenchcoatRobots.Classes {
	/// <summary>
    /// This is a GameObject. It has a position, angle, a list of components, a list of tags and the ability to be inactive.
	/// </summary>
	class GameObject {
		#region Properties
		public Vector2 Position { get; set; }
		public float Angle { get; set; }
		public bool Active { get; set; }
		public List<Classes.Component> Components { get; set; }
		public bool Destroy { get; set; }
        public List<string> Tags { get; set; }
		#endregion

		#region Constructors
		/// <summary>
		/// Creates a new GameObject with position and Angle.
		/// </summary>
		/// <param name="position">Position of the GameObject</param>
		/// <param name="Angle">Angle of the GameObject</param>
		public GameObject( Vector2 position, float Angle ) {
			this.Position = position;
			this.Angle = Angle;
			this.Active = true;
			this.Components = new List<Component>();
			this.Destroy = false;
            this.Tags = new List<string>();
		}
		/// <summary>
		/// Creates a new GameObject with Angle. Position will be 0,0.
		/// </summary>
		/// <param name="Angle">The Angle of the GameObject</param>
		public GameObject( float Angle ) {
			this.Position = new Vector2();
			this.Angle = Angle;
			this.Active = true;
			this.Components = new List<Component>();
			this.Destroy = false;
            this.Tags = new List<string>();
		}
		/// <summary>
		/// Creates a new GameObject with position. Angle will be 0.
		/// </summary>
		/// <param name="position">Position of the GameObject</param>
		public GameObject( Vector2 position ) {
			this.Position = position;
			this.Angle = 0;
			this.Active = true;
			this.Components = new List<Component>();
			this.Destroy = false;
            this.Tags = new List<string>();
		}
        /// <summary>
        /// Creates a new GameObject with position and a tag. Angle will be 0.
        /// </summary>
        /// <param name="position">Position of the GameObject</param>
        public GameObject(Vector2 position, string tag)
        {
            this.Position = position;
            this.Angle = 0;
            this.Active = true;
            this.Components = new List<Component>();
            this.Destroy = false;
            this.Tags = new List<string>();
            this.Tags.Add(tag);
        }
		/// <summary>
		/// Creates a new GameObject. Position will be 0,0 and Angle will be 0
		/// </summary>
		public GameObject() {
			this.Position = new Vector2();
			this.Angle = 0;
			this.Active = true;
			this.Components = new List<Component>();
			this.Destroy = false;
            this.Tags = new List<string>();
		}
		#endregion

		#region Methods
		/// <summary>
		/// Method to return the GameObject. To be used in delegates.
		/// </summary>
		/// <returns>The GameObject this method belongs to.</returns>
		public GameObject ReturnSelf() {
			return this;
		}

		public virtual void Collided( GameObject otherCopy, CollisionInfo collisionInformation ) {
			foreach ( Classes.Component comp in Components ) {
				comp.Collided( otherCopy, collisionInformation );
			}
		}

		public virtual void Update( ref GameTime gameTime ) {
			foreach ( Classes.Component comp in Components ) {
				comp.Update( ref gameTime );
			}
		}

		#region Component Altering
		/// <summary>
		/// Adds a component to the GameObject
		/// </summary>
		/// <param name="component">The component to add.</param>
		public void AddComponent( Classes.Component component ) {
			if ( component.Single ) {
				foreach ( Classes.Component comp in Components ) {
					Debug.Assert( component.GetType() != comp.GetType(), "Duplicate components", "Only one component of type " + comp.GetType() + " may exist on a single GameObject simultaneously." );
				}
			}
			if ( component != null )
				Components.Add( component );
		}

		/// <summary>
		/// Replaces an already existing component. The component must be of a limited type.
		/// </summary>
		/// <param name="component">The object to use as an replacement.</param>
		public void ReplaceComponent( Classes.Component component ) {
			Debug.Assert( component.Single, "Component not of a limited type" );
			int index = -1;
			for ( int i = 0; i < Components.Count; i++ ) {
				if ( component.GetType() == Components[i].GetType() ) {
					index = i;
					break;
				}
			}
			if ( index != -1 )
				Components.RemoveAt( index );
			Components.Add( component );
		}
		#endregion

		public Component GetComponent<T>() {
			for ( int i = 0; i < Components.Count; i++ ) {
				if ( Components[i].GetType() == typeof( T ) ) {
					return Components[i];
				}
			}
			return null;
		}

		public virtual void Draw( ref SpriteBatch spriteBatch, DrawInfo drawinfo ) {
			foreach ( Classes.Component comp in Components ) {
				comp.Draw( ref spriteBatch, ref drawinfo );
			}
		}
		#endregion
	}
}
